/*
 * BottomBar library for Android
 * Copyright (c) 2016 Iiro Krankka (http://github.com/roughike).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.roughike.bottombar;

import java.util.ArrayList;

import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentState;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

/**
 * BottomBar中的tab
 */
public class BottomBarTab extends DirectionalLayout {
    static final String STATE_BADGE_COUNT = "STATE_BADGE_COUNT_FOR_TAB_";

    private static final long ANIMATION_DURATION = 150;
    private static final float ACTIVE_TITLE_SCALE = 1;
    private static final float INACTIVE_FIXED_TITLE_SCALE = 0.86f;
    private static final float ACTIVE_SHIFTING_TITLELESS_ICON_SCALE = 1.24f;
    private static final float INACTIVE_SHIFTING_TITLELESS_ICON_SCALE = 1f;

    private final int sixDps;
    private final int eightDps;
    private final int sixteenDps;

    BottomBarBadge badge;

    private Type type = Type.FIXED;
    private boolean isTitleless;
    private int iconResId;
    private int iconSelectedResId;
    private String title;
    private float inActiveAlpha;
    private float activeAlpha;
    private int inActiveColor;
    private int activeColor;
    private int barColorWhenSelected;
    private int badgeBackgroundColor;
    private boolean badgeHidesWhenActive;
    private Image iconView;
    private Text titleView;
    private boolean isActive;
    private int indexInContainer;
    private Font titleTypeFace;

    BottomBarTab(Context context) {
        super(context);

        sixDps = ResHelper.dpToPixel(context, 6);
        eightDps = ResHelper.dpToPixel(context, 8);
        sixteenDps = ResHelper.dpToPixel(context, 16);
    }

    void setConfig(Config config) {
        setInActiveAlpha(config.inActiveTabAlpha);
        setActiveAlpha(config.activeTabAlpha);
        setInActiveColor(config.inActiveTabColor);
        setActiveColor(config.activeTabColor);
        setBarColorWhenSelected(config.barColorWhenSelected);
        setBadgeBackgroundColor(config.badgeBackgroundColor);
        setBadgeHidesWhenActive(config.badgeHidesWhenSelected);
        setTitleTypeface(config.titleTypeFace);
    }

    void prepareLayout() {
        if (type == Type.TABLET) {
            LayoutScatter.getInstance(getContext()).parse(getLayoutResource(), this, true);
        }else{
            ComponentContainer tempContainer = (ComponentContainer) LayoutScatter.getInstance(getContext()).parse(
                    getLayoutResource(), null, false);
            ArrayList<Component> childs = new ArrayList<>();
            for (int i = 0; i < tempContainer.getChildCount(); i++) {
                Component child = tempContainer.getComponentAt(i);
                childs.add(child);
            }
            tempContainer.removeAllComponents();
            for (Component child : childs) {
                this.addComponent(child);
            }
        }
        this.setOrientation(VERTICAL);
        this.setAlignment(isTitleless? LayoutAlignment.CENTER : LayoutAlignment.HORIZONTAL_CENTER);
        this.setLayoutConfig(new LayoutConfig(MATCH_CONTENT, MATCH_CONTENT));

        iconView = (Image) findComponentById(ResourceTable.Id_bb_bottom_bar_icon);
        iconView.setPixelMap(iconResId);

        if (type != Type.TABLET && !isTitleless) {
            titleView = (Text) findComponentById(ResourceTable.Id_bb_bottom_bar_title);
            titleView.setVisibility(VISIBLE);

            if (type == Type.SHIFTING) {
                findComponentById(ResourceTable.Id_spacer).setVisibility(VISIBLE);
            }

            updateTitle();
        }


        updateCustomTypeface();

        if (type != Type.SHIFTING) {
            // 添加背景选择器
            ShapeElement pressedElement = new ShapeElement();
            pressedElement.setRgbColor(RgbColor.fromArgbInt(Color.LTGRAY.getValue()));
            pressedElement.setShape(ShapeElement.OVAL);
            ShapeElement normalElement = new ShapeElement();
            normalElement.setRgbColor(RgbColor.fromArgbInt(Color.WHITE.getValue()));
            StateElement stateElement = new StateElement();
            stateElement.addState(new int[]{ComponentState.COMPONENT_STATE_PRESSED},pressedElement);
            stateElement.addState(new int[]{},normalElement);
            setBackground(stateElement);
        }


    }

    private int getLayoutResource() {
        int layoutResource;
        switch (type) {
            case FIXED:
                layoutResource = ResourceTable.Layout_bb_bottom_bar_item_fixed;
                break;
            case SHIFTING:
                layoutResource = ResourceTable.Layout_bb_bottom_bar_item_shifting;
                break;
            case TABLET:
                layoutResource = ResourceTable.Layout_bb_bottom_bar_item_fixed_tablet;
                break;
            default:
                // should never happen
                throw new IllegalArgumentException("Unknown BottomBarTab type.");
        }
        return layoutResource;
    }

    private void updateTitle() {
        if (titleView != null) {
            titleView.setText(title);
        }
    }


    private void updateCustomTypeface() {
        if (titleTypeFace != null && titleView != null) {
            titleView.setFont(titleTypeFace);
        }
    }

    Type getType() {
        return type;
    }

    void setType(Type type) {
        this.type = type;
    }

    boolean isTitleless() {
        return isTitleless;
    }

    void setIsTitleless(boolean isTitleless) {
        if (isTitleless && getIconResId() == 0) {
            throw new IllegalStateException("This tab is supposed to be " +
                    "icon only, yet it has no icon specified. Index in " +
                    "container: " + getIndexInTabContainer());
        }

        this.isTitleless = isTitleless;
    }

    public ComponentContainer getOuterView() {
        return (ComponentContainer) getComponentParent();
    }

    Image getIconView() {
        return iconView;
    }

    int getIconResId() {
        return iconResId;
    }

    void setIconResId(int iconResId) {
        this.iconResId = iconResId;
    }

    int getIconSelectedResId() {
        return iconSelectedResId;
    }

    void setIconSelectedResId(int iconSelectedResId) {
        this.iconSelectedResId = iconSelectedResId;
    }

    Text getTitleView() {
        return titleView;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
        updateTitle();
    }

    public float getInActiveAlpha() {
        return inActiveAlpha;
    }

    public void setInActiveAlpha(float inActiveAlpha) {
        this.inActiveAlpha = inActiveAlpha;

        if (!isActive) {
            setAlphas(inActiveAlpha);
        }
    }

    public float getActiveAlpha() {
        return activeAlpha;
    }

    public void setActiveAlpha(float activeAlpha) {
        this.activeAlpha = activeAlpha;

        if (isActive) {
            setAlphas(activeAlpha);
        }
    }

    public int getInActiveColor() {
        return inActiveColor;
    }

    public void setInActiveColor(int inActiveColor) {
        this.inActiveColor = inActiveColor;

        if (!isActive) {
            setColors(inActiveColor);
        }
    }

    public int getActiveColor() {
        return activeColor;
    }

    public void setActiveColor(int activeIconColor) {
        this.activeColor = activeIconColor;

        if (isActive) {
            setColors(activeColor);
        }
    }

    public int getBarColorWhenSelected() {
        return barColorWhenSelected;
    }

    public void setBarColorWhenSelected(int barColorWhenSelected) {
        this.barColorWhenSelected = barColorWhenSelected;
    }

    public int getBadgeBackgroundColor() {
        return badgeBackgroundColor;
    }

    public void setBadgeBackgroundColor(int badgeBackgroundColor) {
        this.badgeBackgroundColor = badgeBackgroundColor;

        if (badge != null) {
            badge.setColoredCircleBackground(badgeBackgroundColor);
        }
    }

    public boolean getBadgeHidesWhenActive() {
        return badgeHidesWhenActive;
    }

    public void setBadgeHidesWhenActive(boolean hideWhenActive) {
        this.badgeHidesWhenActive = hideWhenActive;
    }

    int getCurrentDisplayedIconColor() {
        Object tag = iconView.getTag();

        if (tag instanceof Integer) {
            return (int) tag;
        }

        return 0;
    }

    int getCurrentDisplayedTitleColor() {
        if (titleView != null) {
            return titleView.getTextColor().getValue();
        }

        return 0;
    }



    public void setBadgeCount(int count) {
        if (count <= 0) {
            if (badge != null) {
                badge.removeFromTab(this);
                badge = null;
            }

            return;
        }

        if (badge == null) {
            badge = new BottomBarBadge(getContext());
            badge.attachToTab(this, badgeBackgroundColor);
        }

        badge.setCount(count);

        if (isActive && badgeHidesWhenActive) {
            badge.hide();
        }
    }

    public void removeBadge() {
        setBadgeCount(0);
    }

    boolean isActive() {
        return isActive;
    }

    boolean hasActiveBadge() {
        return badge != null;
    }

    int getIndexInTabContainer() {
        return indexInContainer;
    }

    void setIndexInContainer(int indexInContainer) {
        this.indexInContainer = indexInContainer;
    }




    public void setTitleTypeface(Font typeface) {
        this.titleTypeFace = typeface;
        updateCustomTypeface();
    }

    public Font getTitleTypeFace() {
        return titleTypeFace;
    }

    void select(boolean animate) {
        isActive = true;

        if (animate) {
            animateIcon(activeAlpha, ACTIVE_SHIFTING_TITLELESS_ICON_SCALE);
            animateTitle(sixDps, ACTIVE_TITLE_SCALE, activeAlpha);
            animateColors(inActiveColor, activeColor);
        } else {
            setTitleScale(ACTIVE_TITLE_SCALE);
            setTopPadding(sixDps);
            setIconScale(ACTIVE_SHIFTING_TITLELESS_ICON_SCALE);
            setColors(activeColor);
            setAlphas(activeAlpha);
        }

        setSelected(true);
        iconView.setPixelMap(iconSelectedResId);
        if (badge != null && badgeHidesWhenActive) {
            badge.hide();
        }
    }

    void deselect(boolean animate) {
        isActive = false;

        boolean isShifting = type == Type.SHIFTING;

        float titleScale = isShifting ? 0 : INACTIVE_FIXED_TITLE_SCALE;
        int iconPaddingTop = isShifting ? sixteenDps : eightDps;

        if (animate) {
            animateTitle(iconPaddingTop, titleScale, inActiveAlpha);
            animateIcon(inActiveAlpha, INACTIVE_SHIFTING_TITLELESS_ICON_SCALE);
            animateColors(activeColor, inActiveColor);
        } else {
            setTitleScale(titleScale);
            setTopPadding(iconPaddingTop);
            setIconScale(INACTIVE_SHIFTING_TITLELESS_ICON_SCALE);
            setColors(inActiveColor);
            setAlphas(inActiveAlpha);
        }

        setSelected(false);
        iconView.setPixelMap(iconResId);
        if (!isShifting && badge != null && !badge.isVisible()) {
            badge.show();
        }
    }

    private void animateColors(int previousColor, int color) {
        AnimatorValue anim = new AnimatorValue();
        anim.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                int evaluate = (int) evaluate(value, previousColor, color);
                setColors(evaluate);
            }
        });
        anim.setDuration(150);
        anim.start();
    }

    private Object evaluate(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
                | (int) ((startR + (int) (fraction * (endR - startR))) << 16)
                | (int) ((startG + (int) (fraction * (endG - startG))) << 8)
                | (int) ((startB + (int) (fraction * (endB - startB))));
    }

    private void setColors(int color) {
        if (iconView != null) {
            iconView.setTag(color);
        }

        if (titleView != null) {
            titleView.setTextColor(new Color(color));
        }
    }

    private void setAlphas(float alpha) {
        if (iconView != null) {
            iconView.setAlpha(alpha);
        }

        if (titleView != null) {
            titleView.setAlpha(alpha);
        }
    }

    void updateWidth(float endWidth, boolean animated) {
        if (!animated) {
            getLayoutConfig().width = (int) endWidth;

            if (!isActive && badge != null) {

                badge.show();
            }
            return;
        }

        float start = getWidth();
        AnimatorValue animator = new AnimatorValue();
        animator.setDuration(150);
        animator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                ComponentContainer.LayoutConfig config = getLayoutConfig();
                if (config == null) {
                    return;
                }
                if (start > endWidth) {
                    value = start - (start - endWidth) * value;
                } else {
                    value = start + (endWidth - start) * value;
                }

                config.width = Math.round(value);
                setLayoutConfig(config);
            }
        });

        // Workaround to avoid using faulty onAnimationEnd() listener
        new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
            @Override
            public void run() {
                if (!isActive && badge != null) {

                    badge.show();
                }
            }
        }, animator.getDuration());
        animator.start();
    }


    private void setTopPaddingAnimated(int start, int end) {
        if (type == Type.TABLET || isTitleless) {
            return;
        }

        AnimatorValue paddingAnimator = new AnimatorValue();
        paddingAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                if (start > end) {
                    value = start - (start - end) * value;
                } else {
                    value = start + (end - start) * value;
                }
                iconView.setPadding(
                        iconView.getPaddingLeft(),
                        (int) value,
                        iconView.getPaddingRight(),
                        iconView.getPaddingBottom()
                );
            }
        });
        paddingAnimator.setDuration(ANIMATION_DURATION);
        paddingAnimator.start();
    }

    private void animateTitle(int padding, float scale, float alpha) {
        if (isTitleless) {
            return;
        }

        setTopPaddingAnimated(iconView.getPaddingTop(), padding);

        titleView.createAnimatorProperty()
                .setDuration(ANIMATION_DURATION)
                .scaleX(scale)
                .scaleY(scale)
                .alpha(alpha)
                .start();
    }

    private void animateIconScale(float scale) {
        iconView.createAnimatorProperty()
                .setDuration(ANIMATION_DURATION)
                .scaleX(scale)
                .scaleY(scale)
                .start();
    }

    private void animateIcon(float alpha, float scale) {
        iconView.createAnimatorProperty()
                .setDuration(ANIMATION_DURATION)
                .alpha(alpha)
                .start();
        if (isTitleless && type == Type.SHIFTING) {
            animateIconScale(scale);
        }
    }

    private void setTopPadding(int topPadding) {
        if (type == Type.TABLET || isTitleless) {
            return;
        }

        iconView.setPadding(
                iconView.getPaddingLeft(),
                topPadding,
                iconView.getPaddingRight(),
                iconView.getPaddingBottom()
        );
    }

    private void setTitleScale(float scale) {
        if (type == Type.TABLET || isTitleless) {
            return;
        }
        titleView.setScaleX(scale);
        titleView.setScaleY(scale);
    }

    private void setIconScale(float scale) {
        if (isTitleless && type == Type.SHIFTING) {
            iconView.setScaleX(scale);
            iconView.setScaleY(scale);
        }
    }

    enum Type {
        FIXED, SHIFTING, TABLET
    }

    public static class Config {
        private final float inActiveTabAlpha;
        private final float activeTabAlpha;
        private final int inActiveTabColor;
        private final int activeTabColor;
        private final int barColorWhenSelected;
        private final int badgeBackgroundColor;
        private final Font titleTypeFace;
        private boolean badgeHidesWhenSelected = true;

        private Config(Builder builder) {
            this.inActiveTabAlpha = builder.inActiveTabAlpha;
            this.activeTabAlpha = builder.activeTabAlpha;
            this.inActiveTabColor = builder.inActiveTabColor;
            this.activeTabColor = builder.activeTabColor;
            this.barColorWhenSelected = builder.barColorWhenSelected;
            this.badgeBackgroundColor = builder.badgeBackgroundColor;
            this.badgeHidesWhenSelected = builder.hidesBadgeWhenSelected;
            this.titleTypeFace = builder.titleTypeFace;
        }

        public static class Builder {
            private float inActiveTabAlpha;
            private float activeTabAlpha;
            private int inActiveTabColor;
            private int activeTabColor;
            private int barColorWhenSelected;
            private int badgeBackgroundColor;
            private boolean hidesBadgeWhenSelected = true;
            private Font titleTypeFace;

            public Builder inActiveTabAlpha(float alpha) {
                this.inActiveTabAlpha = alpha;
                return this;
            }

            public Builder activeTabAlpha(float alpha) {
                this.activeTabAlpha = alpha;
                return this;
            }

            public Builder inActiveTabColor(int color) {
                this.inActiveTabColor = color;
                return this;
            }

            public Builder activeTabColor(int color) {
                this.activeTabColor = color;
                return this;
            }

            public Builder barColorWhenSelected(int color) {
                this.barColorWhenSelected = color;
                return this;
            }

            public Builder badgeBackgroundColor(int color) {
                this.badgeBackgroundColor = color;
                return this;
            }

            public Builder hideBadgeWhenSelected(boolean hide) {
                this.hidesBadgeWhenSelected = hide;
                return this;
            }


            public Builder titleTypeFace(Font titleTypeFace) {
                this.titleTypeFace = titleTypeFace;
                return this;
            }

            public Config build() {
                return new Config(this);
            }
        }
    }
}
